package com.ice.body.hardware.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.copier.Copier;
import com.ice.base.constant.HCNetSDK;
import com.ice.base.model.SysLoginModel;
import com.ice.base.util.BaseUtil;
import com.ice.base.util.RedisUtil;
import com.ice.base.util.ThreadPoolUtil;
import com.ice.body.hardware.dao.DeviceVideoDao;
import com.ice.body.hardware.model.DeviceVideo;
import com.ice.body.business.model.VideoMonitorModel;
import com.ice.body.business.service.VideoMonitorService;
import com.ice.base.dao.BaseDao;
import com.ice.base.service.impl.BaseServiceImpl;
import com.ice.body.hardware.service.DeviceVideoService;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Created with GuoYanBin.
 * Description:业务接口-视频监控 serviceImpl
 * Date: 2021-06-18
 * Time: 10:49
 */
@Slf4j
@Component
@Service
public class DeviceVideoServiceImpl extends BaseServiceImpl<DeviceVideo> implements DeviceVideoService{
    @Resource 
    private DeviceVideoDao dao;
    @Override
    public BaseDao<DeviceVideo> getBaseDao() {
        return dao;
    }
    @Resource
    private RedisUtil redisUtil;

    @Resource
    private VideoMonitorService videoMonitorService;

    // 接口的实例，通过接口实例调用外部dll/so的函数
    static HCNetSDK sdk = HCNetSDK.INSTANCE;
    static int lUserID;
    static int lPreviewHandle;

    /**
     * Description 新版本SDK开启录像
     * @param wareHouseId
     */
    @Override
    public void makeVideo (String wareHouseId){
        //查询固定房间的摄像头
        DeviceVideo videoModel = new DeviceVideo();
        videoModel.setWarehouseId(wareHouseId);
        ArrayList<DeviceVideo> list = this.selectListByModel(videoModel);
        // 初始化
        if (!sdk.NET_DVR_Init() || CollectionUtils.isEmpty(list)) {
            System.out.println("开启监控初始化失败或房间号配置错误");
            return;
        }
        //设置不切片
        HCNetSDK.NET_DVR_LOCAL_GENERAL_CFG dvr = new HCNetSDK.NET_DVR_LOCAL_GENERAL_CFG();
        dvr.read();
        dvr.byNotSplitRecordFile = 1;
        dvr.i64FileSize = dvr.size();
        dvr.write();
        Pointer pointer = dvr.getPointer();
        if (sdk.NET_DVR_SetSDKLocalCfg(17,pointer) == false) {//通用参数配置
            System.out.println("绑定失败，错误码为" + sdk.NET_DVR_GetLastError());
        }
        //设置连接时间与重连时间
        sdk.NET_DVR_SetConnectTime(2000, 1);
        sdk.NET_DVR_SetReconnect(10000, true);
        int lUserID;
        //注册设备
        HCNetSDK.NET_DVR_DEVICEINFO m_strDeviceInfo= new HCNetSDK.NET_DVR_DEVICEINFO();
        VideoMonitorModel model;

        HCNetSDK.NET_DVR_CLIENTINFO m_strClientInfo = new HCNetSDK.NET_DVR_CLIENTINFO();
        m_strClientInfo.lChannel = 1;//通道号
        //0表示主码流，为1表示子码流
        m_strClientInfo.lLinkMode = 1;

        try{
            HashMap<Integer,Integer>  vMap = new HashMap<>();
            for (DeviceVideo v:list) {
                lUserID = sdk.NET_DVR_Login(v.getIp(),(short)Integer.parseInt(v.getPort()),v.getUserName(),v.getPwd(),m_strDeviceInfo);
                if (lUserID < 0){
                    System.out.println("摄像头用户登录失败,错误码:" + sdk.NET_DVR_GetLastError());
                    continue;
                }

                lPreviewHandle = sdk.NET_DVR_RealPlay_V30(lUserID,m_strClientInfo,null, null, true);

                vMap.put(lUserID,lPreviewHandle);

                Thread.sleep(1);
                LocalDateTime now =  LocalDateTime.now();
                String fileName = DateUtil.format(now, "yyyyMMddHHmmssSSS") + ".mp4";
                String videoPath =  BaseUtil.getVideoPath() + fileName;

                boolean b = sdk.NET_DVR_SaveRealData_V30(lPreviewHandle, 2, videoPath);
                System.out.println("保存视频：" +b);

                model = new VideoMonitorModel();
                model.setWarehouseId(v.getWarehouseId());
                model.setIp(v.getIp());
                model.setWarehouseName(v.getWarehouseName());
                model.setStartTime(now.toString());
                model.setCreateTime(new Date());
                model.setVideoPath("/video/"+fileName);
                if (videoMonitorService.addVideoSelective(model)) {// 添加视频记录
                    System.out.println("数据添加成功");
                }
            }
            redisUtil.set(wareHouseId+"vMap",vMap);
            /////////////////////////////////////////////////////////////////////////////////////////////////
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void logout(String wareHouseId){
     /*   DeviceVideoModel videoModel = new DeviceVideoModel();
        videoModel.setWarehouseId(wareHouseId);
        ArrayList<DeviceVideoModel> list = this.selectListByModel(videoModel);
        */
        HashMap<String,Integer> vMap = (HashMap)redisUtil.get(wareHouseId+"vMap");
        if(!CollectionUtils.isEmpty(vMap)){
            for (Map.Entry<String, Integer> entry : vMap.entrySet()) {
                String user = entry.getKey();
                Integer value = entry.getValue();
                int userId = Integer.parseInt(user);

                if (userId  >= 0){
                    if (sdk.NET_DVR_Logout(userId) == false) {
                        System.out.println("注销失败，错误码为" + sdk.NET_DVR_GetLastError());
                    }
                    sdk.NET_DVR_StopRealPlay(value);
                    //sdk.NET_DVR_Cleanup();
                    log.info("注销成功");

                }else{
                    System.out.println("设备未登录");
                    //sdk.NET_DVR_Cleanup();
                    return;
                }
            }
            redisUtil.del(wareHouseId + "vMap");
        }

    }

    @Override
    public DeviceVideo getByIp(String ip) {
        return dao.getByIp(ip);
    }


    @Override
    public ArrayList<DeviceVideo> getDistinctWarehouse() {
        return dao.getDistinctWarehouse();
    }

    /**
     * 老版本录像
     * @param bol
     */
    @Override
    public void setVideos(boolean bol) {
        List<HashMap<String,Object>> videoList = dao.selectVideoList(null);
        for(HashMap<String,Object> videoMap: videoList){
            redisUtil.set(videoMap.get("v_code").toString(),bol);
        }
    }

    /**
     * 老版本录像 取代页面请求，如果多次刷页面录像会无限开启
     */
    @Override
    public void makeVideoOa() {
        ThreadPoolExecutor threadPool = ThreadPoolUtil.getThreadPool();
        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    List<HashMap<String, Object>> videoList = dao.selectVideoList(null);
                    for (HashMap<String, Object> videoMap : videoList) {
                        redisUtil.set(videoMap.get("v_code").toString(), false);
                        String rtspPath = "rtsp://" + videoMap.get("userName") + ":" + videoMap.get("pwd") + "@" + videoMap.get("ip") + ":" + videoMap.get("port") + "/Streaming/Channels/101";
                        int audioRecord = 1; // 0代表不录制，1代表录制
                        makeRecord(BaseUtil.getVideoPath(), rtspPath, audioRecord, videoMap.get("v_code").toString(), videoMap.get("location").toString());// 进行视频录制
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    /**
     * 录制视频 老系统拿过来的 图片拼视频
     */
    public void makeRecord(String pathVal,String rtspPath,int audioRecord,String vCode,String location) {
        try {
            VideoMonitorModel model = new VideoMonitorModel();
          //  model.setLocation(location);
            List<SysLoginModel> userMapList = (List<SysLoginModel>)redisUtil.get("userList");
            if(null != userMapList && userMapList.size() != 0){
                model.setUserId(userMapList.get(0).getId().toString());
            }
            int width = 640, height = 480;
            FFmpegFrameGrabber grabber = FFmpegFrameGrabber.createDefault(rtspPath);
            grabber.setOption("rtsp_transport", "tcp"); // 使用tcp的方式，不然会丢包很严重
            grabber.setImageWidth(width);
            grabber.setImageHeight(height);
            log.info("==========grabber start==========");
            grabber.start();

            String videoPath = "/video/" + DateUtil.format(new Date(), "yyyyMMddHHmmss") +vCode+ ".mp4";
            pathVal = BaseUtil.getVideoPath() + videoPath;

            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(pathVal, width, height, audioRecord);
            recorder.setInterleaved(true);
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.setFormat("mp4");
            recorder.setFrameRate(8);
            recorder.setPixelFormat(0);
            log.info("==========recorder start==========");
            recorder.start();
            log.info("==========all start==========");
            Frame frame = null;
            model.setStartTime(DateUtil.formatDateTime(new Date()));
            while (true) {
                Boolean b1 = (boolean) redisUtil.get(vCode);
                if ((boolean)redisUtil.get(vCode)) {
                    //防止重复录制视频，添加视频时设置的true
                    redisUtil.set(vCode,false);
                    model.setVideoPath(videoPath);
                    model.setEndTime(DateUtil.formatDateTime(new Date()));
                    String videoIdStr = "0";
                    String videoType = "";
                    if (redisUtil.get("videoType") != null) {
                        videoType = String.valueOf(redisUtil.get("videoType"));
                    }
                    if (redisUtil.get("videoId") != null) {
                        videoIdStr = String.valueOf(redisUtil.get("videoId"));
                    }
                    log.info("视频类型："+videoType+",视频业务ID："+videoIdStr);

                    if ("1".equals(videoType)) {
                        model.setBusId(Long.parseLong(videoIdStr));
                        model.setBusType(1);
                    }else if ("2".equals(videoType)) {
                        model.setBusId(Long.parseLong(videoIdStr));
                        model.setBusType(2);
                    }else if ("3".equals(videoType)) {
                        model.setBusId(Long.parseLong(videoIdStr));
                        model.setBusType(3);
                    }

                    if (videoMonitorService.addVideoSelective(model)) {// 添加视频记录
//                        redisUtil.del("videoType");// 删除redis中存储的视频类型
//                        redisUtil.del("videoId");// 删除redis中存储的视频业务ID
                        break;
                    }
                }
                frame = grabber.grabImage();
                if (frame == null) {
                    System.out.println("making video.....null");
                    //重启 FFmpegFrameGrabber
                    grabber.stop();
                    grabber.release();
                    log.info("====@@@@@======grabber temp stoped=========@@@@@");
                    grabber = FFmpegFrameGrabber.createDefault(rtspPath);
                    grabber.setOption("rtsp_transport", "tcp"); // 使用tcp的方式，不然会丢包很严重
                    grabber.setImageWidth(width);
                    grabber.setImageHeight(height);
                    log.info("====@@@@@======grabber restarting=========@@@@@");
                    grabber.start();
                    log.info("====@@@@@======grabber restarted==========@@@@@");
                    continue;
                }
                recorder.record(frame);
            }
            log.info(vCode+"==========end==========");
            long start = System.currentTimeMillis();
            grabber.stop();
            grabber.release();
            recorder.stop();
            recorder.release();
            long end = System.currentTimeMillis();
            log.info(vCode+"==========all stop=========="+(end -start)/1000);
        } catch (Exception e) {
            log.info("==========视频录制失败=========="+vCode);
            e.printStackTrace();
        }
    }
 
}
